﻿using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.Collections;
using System.Linq;
using System.IO;
using System.Text;
using Org.BouncyCastle.Crypto.Signers;

namespace smcrypto
{
    public class SM2Utils
    {
        public static void GenerateKeyPair()
        {
            SM2 sm2 = SM2.Instance;
            AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.GenerateKeyPair();
            ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)key.Private;
            ECPublicKeyParameters ecpub = (ECPublicKeyParameters)key.Public;
            BigInteger privateKey = ecpriv.D;
            ECPoint publicKey = ecpub.Q;

            System.Console.Out.WriteLine("公钥: " + Encoding.ASCII.GetString(Hex.Encode(publicKey.GetEncoded())).ToUpper());
            System.Console.Out.WriteLine("私钥: " + Encoding.ASCII.GetString(Hex.Encode(privateKey.ToByteArray())).ToUpper());
        }
        //SM2 加解密联调时走过的坑
        //1、密文数据，有些加密硬件出来密文结构为 C1|C2|C3 ，有些为 C1|C3|C2 , 需要对应密文结构做解密操作
        //2、有些加密硬件，公钥前加04 ，私钥前加00，密文前加04 ，在处理时候，可以根据长度处理，尤其 04 的处理。
        public static String Encrypt(byte[] publicKey, byte[] data, ModeSM2 modeSM2, Encoding encoding)
        {
            if (null == publicKey || publicKey.Length == 0)
            {
                return null;
            }
            if (data == null || data.Length == 0)
            {
                return null;
            }

            byte[] source = new byte[data.Length];
            Array.Copy(data, 0, source, 0, data.Length);

            Cipher cipher = new Cipher();
            SM2 sm2 = SM2.Instance;

            ECPoint userKey = sm2.ecc_curve.DecodePoint(publicKey);

            ECPoint c1 = cipher.Init_enc(sm2, userKey);
            cipher.Encrypt(source);

            byte[] c3 = new byte[32];
            cipher.Dofinal(c3);

            String sc1 = encoding.GetString(Hex.Encode(c1.GetEncoded()));
            String sc2 = encoding.GetString(Hex.Encode(source));
            String sc3 = encoding.GetString(Hex.Encode(c3));

            if (modeSM2 == ModeSM2.c1c2c3)
            {
                return (sc1 + sc2 + sc3).ToUpper();
            }
            return (sc1 + sc3 + sc2).ToUpper();

        }

        public static byte[] Decrypt(byte[] privateKey, byte[] encryptedData,ModeSM2 modeSM2, Encoding encoding)
        {
            if (null == privateKey || privateKey.Length == 0)
            {
                return null;
            }
            if (encryptedData == null || encryptedData.Length == 0)
            {
                return null;
            }

            String data = encoding.GetString(Hex.Encode(encryptedData));

            byte[] c1Bytes = Hex.Decode(encoding.GetBytes(data.Substring(0, 130)));
            byte[] c2 = null;
            byte[] c3 = null;
            if (modeSM2==ModeSM2.c1c2c3)
            {
                int c2Len = encryptedData.Length - 97;
                c2 = Hex.Decode(encoding.GetBytes(data.Substring(130, 2 * c2Len)));
                c3 = Hex.Decode(encoding.GetBytes(data.Substring(130 + 2 * c2Len, 64)));
            }else
            {
               c3 = Hex.Decode(encoding.GetBytes(data.Substring(130, 64)));
               c2 = Hex.Decode(encoding.GetBytes(data.Substring(194)));
            }
           

            SM2 sm2 = SM2.Instance;
            BigInteger userD = new BigInteger(1, privateKey);

            ECPoint c1 = sm2.ecc_curve.DecodePoint(c1Bytes);
            Cipher cipher = new Cipher();
            cipher.Init_dec(userD, c1);
            cipher.Decrypt(c2);
            cipher.Dofinal(c3);

            return c2;
        }


        public static byte[] Sign(byte[] privateKey, byte[] sourceData, byte[] userId=null)
        {
            SM2 sm2Param = SM2.Instance;
            var sm2 = new SM2Signer(new SM3Digest());
            ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(new BigInteger(1, privateKey), sm2Param.ecc_bc_spec);
            ICipherParameters cp;
            if (userId != null) cp = new ParametersWithID(new ParametersWithRandom(privateKeyParameters), userId);
            else cp = new ParametersWithRandom(privateKeyParameters);
            sm2.Init(true, cp);
            sm2.BlockUpdate(sourceData, 0, sourceData.Length);
            return sm2.GenerateSignature();
        }
       

        /// <summary>
        /// 验签
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="signature"></param>
        /// <param name="publicKey"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public static bool VerifySign(byte[] msg, byte[] signature, byte[] publicKey, byte[] id = null)
        {
            SM2 sm2Param = SM2.Instance;
            var sm2 = new SM2Signer(new SM3Digest());
            ECPublicKeyParameters publicKeyParameters= new ECPublicKeyParameters(sm2Param.ecc_curve.DecodePoint(publicKey), sm2Param.ecc_bc_spec);
            ICipherParameters cp;
            if (id != null) cp = new ParametersWithID(publicKeyParameters, id);
            else cp = publicKeyParameters;
            sm2.Init(false, cp);
            sm2.BlockUpdate(msg, 0, msg.Length);
            return sm2.VerifySignature(signature);
        }





        //[STAThread]
        //public static void Main()
        //{
        //    GenerateKeyPair();

            //    String plainText = "ererfeiisgod";
            //    byte[] sourceData = Encoding.Default.GetBytes(plainText);

            //    //下面的秘钥可以使用generateKeyPair()生成的秘钥内容  
            //    // 国密规范正式私钥  
            //    String prik = "3690655E33D5EA3D9A4AE1A1ADD766FDEA045CDEAA43A9206FB8C430CEFE0D94";
            //    // 国密规范正式公钥  
            //    String pubk = "04F6E0C3345AE42B51E06BF50B98834988D54EBC7460FE135A48171BC0629EAE205EEDE253A530608178A98F1E19BB737302813BA39ED3FA3C51639D7A20C7391A";

            //    System.Console.Out.WriteLine("加密: ");
            //    String cipherText = SM2Utils.Encrypt(Hex.Decode(pubk), sourceData);
            //    System.Console.Out.WriteLine(cipherText);
            //    System.Console.Out.WriteLine("解密: ");
            //    plainText = Encoding.Default.GetString(SM2Utils.Decrypt(Hex.Decode(prik), Hex.Decode(cipherText)));
            //    System.Console.Out.WriteLine(plainText);

            //    Console.ReadLine();
            //}
        }
}
